package main

import (
	"context"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
	"gopkg.in/tylerb/graceful.v1"
)

func main() {
	router := gin.Default()
	// 1.静态资源映射
	// router.Static     指定某个目录为静态资源目录，可直接访问这个目录下的资源，url 要具体到资源名称。
	// router.StaticFS   比前面一个多了个功能，当目录下不存 index.html 文件时，会列出该目录下的所有文件。
	// router.StaticFile 指定某个具体的文件作为静态资源访问。
	// 前端项目静态资源
	router.StaticFile("/", "./static/dist/index.html")
	router.StaticFile("/favicon.ico", "./static/dist/favicon.ico")
	router.Static("/assets", "./static/dist/assets")
	// 其他静态文件资源
	router.Static("/public", "./static")
	router.Static("/storage", "./storage/app/public")

	// 2.HTML 渲染展示
	// 方式1.使用 LoadHTMLFiles 加载HTML文件的一部分，并将结果与HTML呈现器相关联。
	router.LoadHTMLFiles("templates/index/index.tmpl")
	router.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index/index.tmpl", gin.H{"title": "HTML 渲染 示例1"})
	})

	// LoadHTMLGlob加载由glob模式标识的HTML文件，并将结果与HTML呈现器相关联，并使用不同目录下名称相同的模板。
	router.LoadHTMLGlob("templates/**/*") //: 注意其目录层级用**表示，文件用*表示
	router.GET("/posts/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
			"title": "这是一篇文章标题",
			"page":  "欢迎访问【全栈工程师修炼指南】公众号，以及作者博客站点：https://blog.weiyigeek.top",
			"date":  time.DateTime,
		})
	})

	// 自定义模板功能
	// 自定义分隔
	router.Delims("{[{", "}]}")
	// 自定义模板中的变量函数，此处使用SetFuncMap设置用于template.FuncMap的FuncMap。
	router.SetFuncMap(template.FuncMap{
		"formatAsDate": formatAsDate,
	})
	// 手动加载模板文件，当然也可以使用LoadHTMLGlob
	router.LoadHTMLFiles("./templates/users/index.tmpl")
	router.GET("/users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "users/index.tmpl", map[string]interface{}{
			"now": time.Date(2023, 06, 10, 0, 0, 0, 0, time.UTC),
		})
	})

	// 使用 graceful 管理 Gin 服务从而优雅的停止
	srv := &graceful.Server{
		Timeout: 10 * time.Second,
		Server: &http.Server{
			Addr:    ":8080",
			Handler: router,
		},
	}

	// 开启一个goroutine启动服务 启动 HTTP Server
	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()

	// 等待中断信号
	quit := make(chan os.Signal)
	// kill 默认会发送 syscall.SIGTERM 信号
	// kill -2 发送 syscall.SIGINT 信号，我们常用的Ctrl+C就是触发系统SIGINT信号
	// kill -9 发送 syscall.SIGKILL 信号，但是不能被捕获，所以不需要添加它
	// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信号转发给quit
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞
	<-quit                                               // 阻塞在此，当接收到上述两种信号时才会往下执行
	log.Println("Shutdown Server ...")

	// 创建一个 5 秒的超时上下文
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// 关闭 HTTP Server
	// 	// 5秒内优雅关闭服务（将未处理完的请求处理完再关闭服务），超过5秒就超时退出
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatal("Server Shutdown:", err)
	}
	log.Println("Server exiting")
}

func formatAsDate(t time.Time) string {
	year, month, day := t.Date()
	return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}
